<script lang="ts" setup>
import { computed, h, onBeforeUnmount, onMounted, reactive, ref } from 'vue'
import {
  CaretDownOutlined,
  CaretUpOutlined,
  ExclamationCircleOutlined,
  LoadingOutlined,
  RedoOutlined,
} from '@ant-design/icons-vue'
import dayjs from 'dayjs'
import type { TableColumnsType, TablePaginationConfig } from 'ant-design-vue'
import { Modal, message } from 'ant-design-vue'
import { storeToRefs } from 'pinia'
import type { SorterResult } from 'ant-design-vue/es/table/interface'
import { useI18n } from 'vue-i18n'
import RepoTask from './components/RepoTask.vue'
import CreateTaskDrawer from './components/CreateTaskDrawer.vue'
import Repos from './components/Repo.vue'
import TaskModal from './components/TaskModal.vue'
import { orderMap } from './config'
import type { Direction, Host } from '@/api'
import { api } from '@/api'
import PageWrapper from '@/components/PageWrapper.vue'
import { useClusterStore, useRepoStore } from '@/store'

const hostListCollapseKey = ref(['host'])
const searchKey = ref()

const { repos } = storeToRefs(useRepoStore())
const { queryRepos } = useRepoStore()
const { permissions } = storeToRefs(useClusterStore())

const { t } = useI18n()
// #region ----------------------------------------< host table >----------------------------------------
const hostTableColumns = computed<TableColumnsType>(() => [
  {
    dataIndex: 'host_name',
    key: 'host_name',
    title: t('vul.hostName'),
  },
  {
    dataIndex: 'host_ip',
    key: 'host_ip',
    title: t('vul.ip'),
  },
  {
    dataIndex: 'cluster_name',
    key: 'cluster_name',
    title: t('vul.cluster'),
    filters: permissions.value?.map(({ cluster_id, cluster_name }) => ({
      text: cluster_name,
      value: cluster_id,
    })),
  },
  {
    dataIndex: 'host_group_name',
    key: 'host_group_name',
    title: t('vul.hostGroup'),
    filters: permissions.value?.map(({ cluster_id, cluster_name, host_groups }) => ({
      text: cluster_name,
      value: cluster_id,
      children: host_groups.map(h => ({ text: h.host_group_name, value: h.host_group_id })),
    })),
    filterMode: 'tree',
    filterSearch: true,
  },
  {
    dataIndex: 'repo',
    key: 'repo',
    title: 'CVE REPO',
    filters: [
      ...repos.value.map(item => ({
        text: item.repo_name,
        value: item.repo_id,
      })),
      { text: t('vul.notSet'), value: t('vul.notSet') },
    ],
  },
  {
    dataIndex: 'cve_num',
    key: 'cve_num',
    title: `${t('vul.unfixed')}/${t('vul.fixed')} CVE`,
    align: 'center',
  },
  {
    dataIndex: 'last_scan',
    key: 'last_scan',
    title: t('vul.lastScan'),
    sorter: true,
  },
])
const pagination = reactive({
  total: 0,
  current: 1,
  pageSize: 10,
  showTotal: (total: number) => t('common.total', { count: total }),
  showSizeChanger: true,
  pageSizeOptions: ['10', '20', '30', '40'],
})
const tableState = reactive<{
  isDrawerVisible: boolean
  selectedRowKeys: string[]
  loading: boolean
  countdown: number
  isSuccessModalVisible: boolean
}>({
  selectedRowKeys: [],
  loading: false,
  isDrawerVisible: false,
  countdown: 0,
  isSuccessModalVisible: false,
})

const filterMap = reactive<{
  host_group_ids?: string[]
  repo?: string[]
  cluster_list?: string[]
}>({
})

const sorterMap = reactive<{
  sortKey: string | undefined
  order: Direction | undefined
}>({
  sortKey: undefined,
  order: undefined,
})

const hostTableData = ref<Host[]>([])
const selectedTableData = computed(() =>
  hostTableData.value.filter(({ host_id }) => tableState.selectedRowKeys.includes(host_id)),
)

async function queryHosts() {
  tableState.loading = true

  const [, res] = await api.getCveHosts({
    page: pagination.current,
    per_page: pagination.pageSize,
    sort: sorterMap.sortKey,
    direction: sorterMap.order,
    filter: {
      host_group_ids: filterMap.host_group_ids,
      repo: filterMap.repo,
      cluster_list: filterMap.cluster_list,
      host_name: searchKey.value || undefined,
    },
  })
  if (res) {
    hostTableData.value = res.result
    pagination.total = res.total_count
  }
  queryHostStatus(hostTableData.value.map(item => item.host_id))

  tableState.loading = false
}

function handleTableChange(page: TablePaginationConfig, filters: Record<string, string[] | null>, sorter: SorterResult<Host>) {
  page.current && (pagination.current = page.current)
  page.pageSize && (pagination.pageSize = page.pageSize)
  sorterMap.sortKey = sorter.column && (sorter.columnKey as string)
  sorterMap.order = sorter.order ? orderMap[sorter.order] : undefined
  filterMap.host_group_ids = filters.host_group_name ? filters.host_group_name : undefined
  filterMap.repo = filters.repo ? filters.repo : undefined
  filterMap.cluster_list = filters.cluster_name ? filters.cluster_name : undefined
  queryHosts()
}

function onSelectChange(selectedRowKeys: string[]) {
  tableState.selectedRowKeys = selectedRowKeys
}

function refresh() {
  tableState.selectedRowKeys = []
  queryHosts()
}

const isExporting = ref(false)
async function exportCve() {
  isExporting.value = true
  if (tableState.selectedRowKeys.length === 0) {
    message.info(t('vul.message.atLeastOneHost'))
    isExporting.value = false
    return
  }
  const [_] = await api.exportCveInfo(tableState.selectedRowKeys)
  if (_)
    return
  message.success(t('vul.message.exportSuccess'))
  isExporting.value = false
  tableState.selectedRowKeys = []
}

async function handleScan() {
  const selected = tableState.selectedRowKeys.map(i => i)
  const list = selected.length ? hostTableData.value.filter(item => selected.includes(item.host_id))! : hostTableData.value
  Modal.confirm({
    title: selected.length > 0 ? t('vul.message.scanFollowHost') : t('vul.message.scanAllHost'),
    icon: h(ExclamationCircleOutlined),
    content: list.length > 0 ? list.map(item => item.host_name).join(',') : '',
    okType: 'danger',
    okText: t('common.confirm'),
    onOk: async () => {
      try {
        const params: Record<string, { host_list: string[] }> = {}
        list.forEach(({ cluster_id, host_id }) => {
          if (!params[cluster_id])
            params[cluster_id] = { host_list: [host_id] }
          else
            params[cluster_id].host_list.push(host_id)
        })
        const [_] = await api.scanHost(params)
        if (!_)
          tableState.selectedRowKeys = []
        queryHostStatus(list.map(i => i.host_id))
      }
      catch {
      }
    },
  })
}

const isScanning = ref(false)
const scaningHosts = ref<string[]>([])
let timer: NodeJS.Timeout

async function queryHostStatus(hostList: string[]) {
  const parseHostStatus = (hostStatus: Record<string, number>) => {
    const scanHostList: string[] = []
    Object.keys(hostStatus).forEach((key) => {
      if (hostStatus[key] === 3)
        scanHostList.push(key)
    })
    return scanHostList
  }
  const [, res] = await api.getHostStatus(hostList)
  if (res) {
    scaningHosts.value = parseHostStatus(res.result)
    if (scaningHosts.value.length > 0) {
      isScanning.value = true
      timer = setTimeout(() => {
        queryHostStatus(scaningHosts.value)
      }, 5000)
    }
    else {
      if (isScanning.value)
        refresh()
      isScanning.value = false
      timer ?? clearTimeout(timer)
    }
  }
}
// #endregion

onMounted(() => {
  queryHosts()
  queryRepos()
})

onBeforeUnmount(() => {
  if (timer)
    clearInterval(timer)
})
</script>

<template>
  <PageWrapper>
    <a-card>
      <a-collapse v-model:activeKey="hostListCollapseKey" :bordered="false" ghost>
        <a-collapse-panel key="host" :show-arrow="false">
          <template #header>
            <span style="font-size: 16px">{{ $t('router.vulnerability.host') }}</span>
            <CaretUpOutlined v-if="hostListCollapseKey.length" />
            <CaretDownOutlined v-else />
          </template>
          <a-row flex="flex" justify="end">
            <span v-if="scaningHosts.length > 0">
              <LoadingOutlined spin /> {{ t('vul.sentence.isScanning', { count: scaningHosts?.length }) }}
            </span>
          </a-row>
          <a-row type="flex" justify="space-between">
            <a-col>
              <a-input-search v-model:value="searchKey" :placeholder="$t('vul.cveDetail.palceHolder.searchBy')" :maxlength="40" @search="queryHosts" />
            </a-col>
            <a-col>
              <a-row type="flex" :gutter="6">
                <a-col v-if="tableState.selectedRowKeys.length > 0">
                  <a-alert type="info" show-icon class="delete-alert">
                    <template #message>
                      <span>{{ t('common.selectItems', { count: tableState.selectedRowKeys.length }) }}</span>
                      <a-divider type="vertical" />
                      <a @click="tableState.selectedRowKeys = []"> {{ t('common.clear') }}</a>
                    </template>
                  </a-alert>
                </a-col>
              </a-row>
            </a-col>
            <a-col>
              <a-space>
                <CreateTaskDrawer
                  :title="$t('vul.setRepo')"
                  :visible="tableState.isDrawerVisible"
                  @close="tableState.isDrawerVisible = false"
                >
                  <template #trigger>
                    <a-button type="primary" @click="tableState.isDrawerVisible = true">
                      {{ $t('vul.setRepo') }}
                    </a-button>
                  </template>
                  <template #content>
                    <RepoTask
                      :host-list="selectedTableData"
                      :repo-list="repos"
                      @cancel="tableState.isDrawerVisible = false"
                      @close="tableState.isDrawerVisible = false"
                      @success="
                        tableState.isDrawerVisible = false;
                        tableState.isSuccessModalVisible = true;
                        tableState.selectedRowKeys = []
                      "
                    />
                  </template>
                </CreateTaskDrawer>
                <a-button
                  type="primary"
                  @click="handleScan"
                >
                  {{ $t('vul.scan') }}
                </a-button>
                <a-button type="primary" :loading="isExporting" @click="exportCve">
                  {{ $t('common.export') }}
                </a-button>
                <a-button :icon="h(RedoOutlined)" @click="refresh">
                  {{ t('common.refresh') }}
                </a-button>
              </a-space>
            </a-col>
          </a-row>
          <a-table
            row-key="host_id"
            :columns="hostTableColumns"
            :data-source="hostTableData"
            :loading="tableState.loading"
            :pagination="pagination"
            :row-selection="{
              selectedRowKeys: tableState.selectedRowKeys,
              onChange: onSelectChange,
            }"
            @change="handleTableChange"
          >
            <template #bodyCell="{ record, column }">
              <template v-if="column.dataIndex === 'host_name'">
                <router-link
                  :to="{
                    path: `/vulnerability/hosts/host-detail/${record.host_id}`,
                    query: { host_name: record.host_name },
                  }"
                >
                  {{ record.host_name }}
                </router-link>
              </template>
              <template v-if="column.dataIndex === 'repo'">
                {{
                  record.repo_id
                    ? repos?.find((item) => item.repo_id === record.repo_id)?.repo_name || '————'
                    : '————'
                }}
              </template>
              <template v-if="column.dataIndex === 'cve_num'">
                {{ `${record.unfixed_cve_num} / ${record.fixed_cve_num}` }}
              </template>
              <template v-if="column.dataIndex === 'last_scan'">
                {{
                  record.last_scan
                    ? dayjs(record.last_scan * 1000).format('YYYY-MM-DD HH:mm:ss')
                    : t('vul.notScaned')
                }}
              </template>
            </template>
          </a-table>
        </a-collapse-panel>
      </a-collapse>
    </a-card>
    <a-card>
      <Repos />
    </a-card>

    <TaskModal v-model:visible="tableState.isSuccessModalVisible" :title="$t('vul.sentence.repoSetSuccess')" />
  </PageWrapper>
</template>

<style lang="less" scoped>
.ant-collapse {
  :deep(.ant-collapse-header) {
    padding: 0;
  }
}
</style>
